namespace Plugins.Engine
{
    using System;
    using System.Collections.Generic;
    using Plugins.Interfaces;

    [Serializable]
    public class PluginService<T> : PluginService,
        IPluginServiceProvider<IPluginService<T>>,
        IPluginServiceProvider<IPluginService>,
        IPluginService<T>,
        IPluginService
        where T : class, IPluginServiceProvider<T>
    {
        private readonly PluginDelegateMediator _delegateMediator = new PluginDelegateMediator();

        private IDictionary<PluginTypeInstance, ICollection<T>> _items;

        public PluginService()
            : base(typeof(T))
        {
            this._items = new Dictionary<PluginTypeInstance, ICollection<T>>();
        }

        protected void AddKey(PluginTypeInstance key, T value)
        {
            if (key != null)
            {
                if (!this.Contains(key))
                {
                    this._items.Add(key, new List<T>());
                }
                this._items[key].Add(value);
            }
        }

        private bool Contains(PluginTypeInstance key)
        {
            if (key != null)
            {
                return this._items.ContainsKey(key);
            }
            return false;
        }

        private bool Contains(PluginTypeInstance key, T value)
        {
            if (key != null)
            {
                if (this._items.ContainsKey(key))
                {
                    if (!object.Equals(value, default(T)))
                    {
                        return this._items[key].Contains(value);
                    }
                }
            }
            return false;
        }

        public override int GetTypeCount(Type type)
        {
            return GetTypeCount(PluginTypeInstance.Get(type, this));
        }

        protected int GetTypeCount(PluginTypeInstance key)
        {
            if (this.Contains(key))
            {
                return this._items[key].Count;
            }
            return 0;
        }

        public T GetService(Type type, int index)
        {
            return GetService(PluginTypeInstance.Get(type, this), index);
        }

        public IEnumerable<T> GetServices(Type type)
        {
            return GetServiceCollection(PluginTypeInstance.Get(type, this));
        }

        public IEnumerable<T> GetServices()
        {
            return GetServices(typeof(T));
        }

        protected T GetService(PluginTypeInstance key, int index)
        {
            IList<T> items = GetServiceCollection(key);
            if (items != null)
            {
                if ((index >= 0) && (index < this.GetTypeCount(key)))
                {
                    return items[index];
                }
            }
            return default(T);
        }

        protected IList<T> GetServiceCollection(PluginTypeInstance key)
        {
            if (this.Contains(key))
            {
                return new List<T>(this._items[key]);
            }
            return null;
        }

        public override IEnumerable<IPluginObject> GetObjects(Type type)
        {
            int count = GetTypeCount(type);
            List<IPluginObject> collection = new List<IPluginObject>();
            for (int i = 0; i < count; i++)
            {
                IPluginObject obj = GetService(type, i) as IPluginObject;
                if (obj != null)
                {
                    collection.Add(obj);
                }
            }
            return collection;
        }

        public T GetService(int index)
        {
            return GetService(PluginTypeInstance.Get(typeof(T), this), index);
        }

        public bool Register(Type type, T item)
        {
            PluginTypeInstance key = PluginTypeInstance.Register(type, this);
            if (!this.Contains(key, item))
            {
                this.AddKey(key, item);
                return true;
            }
            return false;
        }

        protected bool Register(PluginTypeInstance key, T value)
        {
            if (!this.Contains(key, value))
            {
                this.AddKey(key, value);
                return true;
            }
            return false;
        }

        public bool Register(IPluginServiceManager manager, T item)
        {
            if (item != null)
            {
                Type type = typeof(T);
                PluginTypeInstance key = PluginTypeInstance.Register(type, this);
                if (!this.Contains(key, item))
                {
                    PluginServiceManager.RegisterService<T>(manager, this);
                    this.AddKey(key, item);
                    return true;
                }
            }
            return false;
        }

        protected void RemoveKey(PluginTypeInstance key, T value)
        {
            if (key != null)
            {
                if (this.Contains(key, value))
                {
                    this._items[key].Remove(value);
                    if (this._items[key].Count == 0)
                    {
                        this._items.Remove(key);
                    }
                }
            }
        }

        public bool Unregister(Type type, T item)
        {
            PluginTypeInstance key = PluginTypeInstance.Unregister(type, this);
            if (this.Contains(key, item))
            {
                this.RemoveKey(key, item);
                return true;
            }
            return false;
        }

        protected bool Unregister(PluginTypeInstance key, T value)
        {
            if (this.Contains(key, value))
            {
                this.RemoveKey(key, value);
                return true;
            }
            return false;
        }

        public bool Unregister(IPluginServiceManager manager, T item)
        {
            if (item != null)
            {
                Type type = typeof(T);
                PluginTypeInstance key = PluginTypeInstance.Unregister(type, this);
                if (this.Contains(key, item))
                {
                    PluginServiceManager.UnregisterService<T>(manager, this);
                    this.RemoveKey(key, item);
                    return true;
                }
            }
            return false;
        }

        public int Count
        {
            get
            {
                return GetTypeCount(PluginTypeInstance.Get(typeof(T), this));
            }
        }

        public void Register<TDelegate>(T target, string action, TDelegate delegateMethod)
        {
            if (delegateMethod is Delegate)
            {
                _delegateMediator.Add(target, action, delegateMethod);
            }
        }

        public void Unregister<TDelegate>(T target, string action, TDelegate delegateMethod)
        {
            if (delegateMethod is Delegate)
            {
                _delegateMediator.Remove(target, action, delegateMethod);
            }
        }

        public void Invoke<TDelegate>(string action, Action<TDelegate> actionMethod)
        {
            _delegateMediator.InvokeAction<TDelegate>(action, actionMethod);
        }
    }
}

